#ifndef _CPROPERTYTABLE_H_
#define _CPROPERTYTABLE_H_

#include <buola/widgets/cwidget.h>

#include <buola/utility/usignal.h>

#include <unordered_map>

namespace buola { namespace gui {

class CPropertyTable : public CWidget
{
public:
    struct SRow;
    struct SGroup;

    enum
    {
        STYLE_NOICON=0x00000100
    };

    enum EFieldFlags
    {
        FIELD_NONE=0,
        FIELD_VARIANT=0x01,
        FIELD_HIDDEN=0x02,
        FIELD_READONLY=0x04
    };

    enum EGroupFlags
    {
        GROUP_NONE=0,
        GROUP_STYLE=0xffff,
        GROUP_NOHEADER=0x0001,
        GROUP_ADDBUTTON=0x0002,
        GROUP_HIDEIFEMPTY=0x0004,
        GROUP_STATE=0xffff0000,
        GROUP_HLBUTTON=0x00010000
    };
    DECLARE_FRIEND_ENUM_BITOPS(EGroupFlags)

    static const TID DEFAULT_GROUP=0;

    struct SField : public UUncopiable
    {
        EFieldFlags mFlags;
        CPropertyType *mType;
        std::wstring mTitle;

        int mValueIndex;
        int mFlagsIndex;
        int mTypeIndex;

        double mX;
        double mW;
        double mMinW;

        void InitRow(SRow*);
    };

    struct SPosition
    {
        SGroup *mGroup;
        SRow *mRow;
        SField *mField;
    };

    struct SGroup : public UUncopiable
    {
        TID mID;
        std::wstring mName;
        EGroupFlags mFlags;
        std::vector<std::unique_ptr<SRow>> mRows;
        void OnFieldAdded(CPropertyType *pType);
        void OnVariantFieldAdded(CPropertyType *pDefaultType);
        void OnFieldsRemoved();

        double mYStart;
        double mYBase;
        double mYEnd;
    };

    struct SRow : public UUncopiable
    {
        double mY;
        int mRowI;
        int mGroup;
        std::vector<std::unique_ptr<UAny>> mValues;
        std::vector<EFieldFlags> mFlags;
        std::vector<CPropertyType*> mTypes;
    };

public:
    CPropertyTable();
    ~CPropertyTable();

//managing fields
    TID AddField(CPropertyType *pType,const std::wstring &pTitle,double pW=100);
    TID AddField(const std::wstring &pType,const std::wstring &pTitle,double pW=100);
    TID AddVariantField(CPropertyType *pType,const std::wstring &pTitle,double pW=100);
    TID AddVariantField(const std::wstring &pType,const std::wstring &pTitle,double pW=100);
    void RemoveAllFields();

//managing groups
    TID AddGroup(const std::wstring &pName,EGroupFlags pFlags=GROUP_NONE);
    void RemoveAllGroups();
    void SetGroupFlags(TID pGroup,EGroupFlags pFlags);
    void SetGroupName(TID pGroup,const std::wstring &pName);

//managing rows
    SRow *AddRow(TID pGroup=0);
    void RemoveRow(SRow *pRow);
    void RemoveAllRows();

    int GetRowCount(TID pGroup=0);
    SRow *GetRow(int pIndex,TID pGroup=0);

//managing cells
    const UAny &GetCellValue(SRow *pRow,TID pField);
    const UAny &GetCellValue(SRow *pRow,const std::wstring &pField);

    void SetCellValue(SRow *pRow,TID pField,const UAny &pValue);
    void SetCellValue(SRow *pRow,const std::wstring &pField,const UAny &pValue);

    void SetCellType(SRow *pRow,TID pField,CPropertyType *pType);
    void SetCellType(SRow *pRow,const std::wstring &pField,CPropertyType *pType);

    void SetCellFlags(SRow *pRow,TID pField,EFieldFlags pFlags);
    void SetCellFlags(SRow *pRow,const std::wstring &pField,EFieldFlags pFlags);

    void EditCell(SRow *pRow,TID pField);
    void EditCell(SRow *pRow,const std::wstring &pField);

    void CenterCell(SRow *pRow,TID pField);

    void SetIconStyle(int);

    SRow *GetSelRow();

public:
////////////// Event handling //////////////
    virtual void OnExpose(const CExposeEvent &pE);
    void OnHScroll(int pPos);
    void OnVScroll(int pPos);
    virtual void OnButtonDown(const CMouseEvent &pE);
    virtual void OnButtonUp(const CMouseEvent &pE);
    virtual void OnMouseMove(const CMouseEvent &pE);
    virtual void OnMouseWheel(const CMouseEvent &pE);
    virtual void OnSize(const CGeometryEvent &pE);

    virtual void OnStyleChanged(TStyle);

    virtual void OnFillMenu(PMenu,bool) {}

    void OnRulerMoved(TID pID,double pSize);

protected:
/////////////// protected functions //////////////
    void DrawAll(img::CGC*);
    void DrawAll(img::CGC*,const CRect_d &pRect);
    void DrawCell(img::CGC*,SRow *pRow,TID pCol,const CRect_d &pRect);

    void ShapeAll();

    void PosAll();

    void Update();

    void FillRuler();

    SPosition GetCellAt(const CPoint_d&);

    void StartEditing(SRow *pRow,SField *pField);
    void EndEditing();

public:
    USignal<void(SRow*,SField*,UAny)> sChanged;
    USignal<void(SRow*,TID)> sRowAdded;

private:
    int mLastRowI;
    double mCellH;
    double mHeaderH;
    CSize_d mIconSize;
    CSize_d mTextSize;

    PRulerBox mRuler;
    PScrollBar mHScroll;
    PScrollBar mVScroll;

    SPosition mSelected;
    SPosition mEditing;
    CPropertyEditor *mEditor;

    bool mIsPosValid;
    bool mIsDrawing;
    SGroup *mButtonSelected;
    SGroup *mButtonPressed;

    int mIconStyle;

    int mNextValueIndex;
    int mNextFlagsIndex;
    int mNextTypeIndex;

    std::vector<std::unique_ptr<SGroup>> mGroups;
    std::vector<std::unique_ptr<SField>> mFields;
    std::unordered_map<std::wstring,SField*> mFieldsHash;
    
    CPoint_d mScrollPos;
};

/*namespace gui*/ } /*namespace buola*/ }

#endif
